ADB 생산성 향상을 위한 Shell Script CLI 도구 개발기

들어가며

안드로이드 개발을 하다 보면 ADB 명령어를 반복적으로 사용하게 됩니다. 특히 여러 디바이스에 APK를 설치하거나, 앱 정보를 확인하는 작업은 매번 긴 명령어를 입력해야 해서 번거로웠습니다.

그래서 개인적으로 사용할 목적으로 간단한 쉘 스크립트 두 개를 만들어 보았습니다. 혹시 비슷한 불편함을 겪고 계신 분들께 도움이 될 수도 있을 것 같아 소개해드립니다.

결과물 소개

다운로드: https://github.com/Claude-H/adb-extensions

두 개의 스크립트로 구성되어 있습니다:

  • ai.sh: APK 설치 전용 (ADB Installer)
  • ak.sh: APK 관리 및 디바이스 제어 (ADB Kit)

만들게 된 계기

평소 개발하면서 이런 상황들이 자주 있었습니다:

  • 여러 디바이스에 같은 APK 설치할 때마다 adb devices → 디바이스 ID 복사 → adb -s DEVICE_ID install app.apk 반복
  • 포그라운드 앱의 패키지명을 몰라서 adb shell dumpsys activity 명령어로 찾아야 하는 번거로움
  • APK 설치 실패 시 -t, -r, -d 같은 옵션들을 하나씩 시도해보기
  • 앱 정보나 권한을 확인하려면 복잡한 dumpsys 명령어 조합 필요

이런 반복 작업들이 귀찮아서 간단한 래퍼 스크립트를 만들어 보기로 했습니다.

설치 및 환경 설정

1. 사전 준비사항

필수 요구사항:

  • Android SDK의 adb 도구 (PATH에 등록되어 있어야 함)
  • Bash 쉘 환경 (macOS/Linux)

선택 요구사항:

  • 서명 해시 추출 기능 사용 시: ANDROID_HOME 환경 변수 설정 필요

2. ANDROID_HOME 설정

서명 분석 기능(ak signature)을 사용하려면 환경 변수 설정이 필요합니다:

# ~/.bashrc 또는 ~/.zshrc에 추가
export ANDROID_HOME="/Users/your-username/Library/Android/sdk"

3. 스크립트 설치

# 저장소 클론
git clone https://github.com/Claude-H/adb-extensions.git
cd adb-extensions

# 자동 설치 (권장)
sudo ./ai.sh --install
sudo ./ak.sh --install

자동 설치 시 다음 작업이 수행됩니다:

  • /usr/local/bin에 스크립트 복사 (ai, ak 명령어로 사용 가능)
  • 실행 권한 부여
  • macOS 격리 속성 제거 (xattr -d com.apple.quarantine)

ai.sh - APK Installer

명령어 사용법

ai [옵션] [apk파일...]

사용 가능한 옵션

기본 옵션

옵션 설명
-v, --version 스크립트 버전 출력
-h, --help 도움말 출력

APK 선택 옵션

옵션 설명
-l 현재 디렉토리에서 가장 최신 APK 설치
-a 현재 디렉토리의 모든 APK 설치
-s 설치할 APK를 사용자에게 선택하도록 인터랙티브 제공

디바이스 타겟팅 옵션

옵션 설명
-m 연결된 모든 디바이스에 APK 설치

ADB 설치 옵션

옵션 설명
-r 기존 앱 덮어쓰기 설치 (adb install -r, 기본값)
-t 테스트 APK 설치 허용
-d 버전 코드 다운그레이드 허용 (패키지 매니저 권한 필요)

주요 기능

가장 자주 쓰는 APK 설치 패턴들을 간단한 옵션으로 만들었습니다:

1. 간편한 APK 선택

스크립트는 세 가지 APK 선택 모드를 지원합니다:

# 최신 APK 자동 선택
ai -l

# 모든 APK 배치 설치
ai -a

# 인터랙티브 선택
ai -s

ai.sh extension 동작 흐름도ai.sh extension 동작 흐름도

동작 흐름

ai.sh는 사용자가 선택한 옵션에 따라 다음과 같은 지능형 설치 프로세스를 수행합니다:

핵심 구현 특징:

  • .idsig 파일 자동 감지: APK와 함께 .idsig 파일이 존재할 경우 --no-incremental 옵션을 자동으로 적용하여 인크리멘탈 설치 문제를 방지
  • 다단계 복구 메커니즘: 설치 실패 시 발생한 에러 유형에 따라 -t, -d 옵션 적용 또는 기존 앱 제거 후 재설치 등, 상황에 맞는 복구 절차를 자동으로 수행함
  • 다중 디바이스 일괄 설치: -m 옵션으로 연결된 모든 디바이스에 일괄 설치 지원

2. 다중 디바이스 관리

복수의 안드로이드 디바이스가 연결된 환경에서 효율적으로 작업할 수 있도록 설계되었습니다:

디바이스 자동 감지 및 선택 처리 흐름디바이스 자동 감지 및 선택 처리 흐름

# 연결된 모든 디바이스에 설치
ai -m app.apk

# 디바이스 선택 UI
present_device_selection() {
  echo -e "${BARROW} ${BOLD}List of connected devices: $device_count${NC}"
  local i=1
  for device_info in "${device_list[@]}"; do
    echo -e "[${BOLD}$i${NC}] ${YELLOW}$(pretty_device $device_info)${NC}"
    ((i++))
  done
}

3. 지능형 설치 실패 복구

설치 실패 시나리오에 대한 자동 복구 메커니즘이 구현되어 있습니다:

설치 실패 자동 복구 시스템 흐름도설치 실패 자동 복구 시스템 흐름도

  1. 기본 설치 시도 (adb install -r)
  2. 테스트 APK 모드 (-t 옵션 추가)
  3. 다운그레이드 허용 (-d 옵션 추가)
  4. 앱 데이터 보전 경고와 함께 재설치 안내
execute_install_command() {
  case "$result" in
    *INSTALL_FAILED_TEST_ONLY*)
      retry_install "INSTALL_FAILED_TEST_ONLY" "-t" "${device_opt}" "${install_opt}" "${apk_file}"
      ;;
    *INSTALL_FAILED_VERSION_DOWNGRADE*)
      if [[ "$install_opt" == *"-d"* ]]; then
        resolve_downgrade "${device_opt}" "${install_opt}" "${apk_file}"
      else
        retry_install "INSTALL_FAILED_VERSION_DOWNGRADE" "-d" "${device_opt}" "${install_opt}" "${apk_file}"
      fi
      ;;
    *INSTALL_FAILED_UPDATE_INCOMPATIBLE*)
      resolve_conflict "${device_opt}" "${install_opt}" "${apk_file}" "${result}"
      ;;
  esac
}

ak.sh - APK 관리 도구

명령어 사용법

ak <명령어> [패키지명] [추가 인자...]

사용 가능한 옵션

기본 옵션

옵션 설명
-v, --version 스크립트 버전 출력
-h, --help 도움말 출력

사용 가능한 명령어

명령어 사용 예시 설명
pull ak pull [packageName] [outputFile] 지정한 앱의 APK 파일을 로컬로 저장합니다. 출력 파일명을 생략하면 [packageName].apk로 저장됩니다.
info ak info [packageName] 앱의 버전, 설치 시점, 데이터 경로 등 핵심 정보를 조회합니다.
permissions ak permissions [packageName] 앱이 요청한 권한 목록을 출력합니다.
uninstall ak uninstall [packageName] 앱을 디바이스에서 제거합니다.
kill ak kill <packageName1> [packageName2 ...] 하나 이상의 앱 프로세스를 강제 종료합니다. 첫 번째 패키지명은 필수이며, 이후는 선택적으로 추가할 수 있습니다.
devices ak devices 연결된 디바이스 목록과 상태 정보를 출력합니다.
launch ak launch <packageName> 앱의 런처 액티비티를 실행합니다.
signature ak signature [packageName] 앱의 SHA-256 서명 해시를 출력합니다. ANDROID_HOME 환경 변수가 설정되어 있어야 합니다.

참고: [packageName]을 생략하면 현재 포그라운드 앱 기준으로 동작합니다.

주요 기능

APK 설치 외에 필요한 관리 작업들을 모아놨습니다:

1. 현재 앱 자동 감지

현재 포그라운드 애플리케이션을 자동으로 감지하는 기능:

detect_foreground_package() {
    adb -s "$G_SELECTED_DEVICE" shell dumpsys activity activities | \
    grep -i Hist | head -n 1 | \
    sed -n 's/.* u0 \([^\/ ]*\)\/.*/\1/p'
}

2. 패키지 유효성 검사

validate_package_or_exit() {
    local package_name="$1"
    if ! validate_package_name "$package_name"; then
        echo -e "${RED}✘ Invalid package name format:${NC} $package_name"
        exit 1
    fi
    if ! is_package_installed "$package_name"; then
        echo -e "${RED}✘ Package not installed on the device:${NC} $package_name"
        exit 1
    fi
}

3. APK 서명 분석 기능

apksigner를 활용한 SHA-256 서명 해시 추출 (ANDROID_HOME 설정 필요):

get_signature_info() {
    # APK 임시 추출
    adb -s "$G_SELECTED_DEVICE" pull "$apk_path" "$tmp_apk" > /dev/null
    
    # apksigner로 서명 정보 추출
    apksigner=$(find "$ANDROID_HOME/build-tools" -name apksigner | sort -V | tail -n 1)
    signature_output=$("$apksigner" verify --print-certs "$tmp_apk" 2>&1)
    
    # 결과 파싱 및 출력
    echo "$signature_output" | grep -v '^WARNING:' | while IFS= read -r line; do
        if echo "$line" | grep -q 'SHA-256'; then
            echo -e "${GREEN}${BOLD}${line}${NC}"
        else
            echo "$line"
        fi
    done
}

구현하면서 신경 쓴 부분들

1. 가독성 있는 출력

터미널에서 한눈에 알아볼 수 있도록 색상으로 구분했습니다:

RED='\033[1;31m'     # 에러
GREEN='\033[1;32m'   # 성공
YELLOW='\033[1;33m'  # 경고/정보
BLUE='\033[1;34m'    # 일반 정보
CYAN='\033[1;36m'    # 강조

2. macOS 환경 고려

개발 환경이 macOS라서 특별히 신경 쓴 부분들이 있습니다:

install_script() {
  local src_path="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
  local filename="${filename%.sh}"  # .sh 확장자 제거
  local dest_path="/usr/local/bin/$filename"
  
  cp "$src_path" "$dest_path"
  chmod +x "$dest_path"
  xattr -d com.apple.quarantine "$dest_path" 2>/dev/null  # macOS 격리 해제
}

3. 사용자 실수 방지

실제 사용하다 보니 자주 하는 실수들을 미리 체크하도록 했습니다:

handle_option_combinations() {
  if [ $opt_l_used -eq 1 ] && [ $opt_a_used -eq 1 ] && [ $opt_s_used -eq 1 ]; then
    echo -e "${ERROR} Options -l, -a, and -s cannot be used together."
    exit 1
  fi
  # ... 추가 검증 로직
}

사용 예시

# 최신 APK를 모든 기기에 설치
ai -l -m

# 포그라운드 앱의 권한 목록 출력
ak permissions

# SHA-256 서명 해시 출력
ak signature com.example.app

간단한 최적화

1. ADB 호출 최소화

디바이스 정보를 한 번에 가져와서 여러 번 파싱하는 방식으로 ADB 호출 횟수를 줄였습니다:

pretty_device() {
    local device_id="$1"
    local props=$(adb -s "$device_id" shell getprop)
    
    # 한 번의 호출로 모든 속성 정보 획득
    brand=$(echo "$props" | awk -F'[][]' '$2 == "ro.product.brand" {print $4}')
    model=$(echo "$props" | awk -F'[][]' '$2 == "ro.product.model" {print $4}')
    version=$(echo "$props" | awk -F'[][]' '$2 == "ro.build.version.release" {print $4}')
    api=$(echo "$props" | awk -F'[][]' '$2 == "ro.build.version.sdk" {print $4}')
}

2. 반복 작업 처리

여러 APK나 디바이스를 처리할 때 중첩 루프로 효율적으로 처리합니다:

for d in "${selected_device[@]}"; do
  for apk_file in "${apk_files[@]}"; do
    execute_install_command "-s $d" "$install_opt" "$apk_file"
  done
done

개발자 경험(DX) 개선 효과

기존 워크플로우 vs 개선된 워크플로우

다중 디바이스 APK 설치 시나리오

기존의 복잡한 ADB 명령어 작업을 단순화하여 개발 생산성을 높인 사례입니다.

기존 ADB 명령 vs. ai 명령: 플로우 차이점 한눈에 보기기존 ADB 명령 vs. ai 명령: 플로우 차이점 한눈에 보기


보안 고려사항

1. 패키지명 검증

정규식을 통한 엄격한 패키지명 형식 검증:

validate_package_name() {
    local pkg="$1"
    [[ "$pkg" =~ ^[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+$ ]]
}

2. 사용자 확인 프로세스

중요한 작업(앱 삭제 등) 전 사용자 확인:

echo -n "Do you want to uninstall and reinstall the application [y/n]? "
stty -echo -icanon
choice=$(dd bs=1 count=1 2>/dev/null)
stty echo icanon

마치며

ADB Extensions는 Shell Script의 강력함과 실용성을 입증하는 프로젝트입니다. 단순해 보이는 도구이지만, Android 개발 워크플로우에서 실질적인 생산성 향상을 달성했습니다. 개인 토이 프로젝트로 시작했지만, 실무에서 충분히 활용 가능한 수준의 완성도와 안정성을 갖추고 있습니다.

실제 사용해보니 좋은 점들:

  • 복잡한 ADB 명령어 조합을 기억할 필요 없음
  • 설치 실패 시 자동으로 다른 옵션 시도
  • 여러 디바이스 작업이 편해짐
  • 현재 실행 중인 앱 자동 감지로 패키지명 찾는 수고 덜어짐

한계:

  • 기본적인 기능만 구현 (복잡한 시나리오는 여전히 직접 ADB 사용 필요)
  • macOS 위주로 테스트 (다른 환경에서는 일부 기능 동작 안 할 수도)

이 프로젝트가 Android 개발 커뮤니티에 기여할 수 있는 작은 도구가 되기를 희망하며, 더 많은 개발자들이 반복적인 작업에서 벗어나 창의적인 개발에 집중할 수 있는 환경을 만드는 데 도움이 되었으면 합니다.